package dbc;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* Special syntax analyzer to handle post-condition's special operators :
* $old and $return
*/ 
public class PostConditionHandler extends SyntaxHandler {
    /** Name of the variable containing return value */
    public static final String RETURN_VARIABLE_NAME = "returned";

    private StringWriter aspectCode;
    private PrintWriter aspectCodeWriter;
    private List<String> oldVariableNames;

    private String className;
    private String paramList;
    private String methodName;
    private boolean isStatic;

    /**
     * @param expression Expression to be analyze
     * @param className Used to generate the name of the aspect file
     * @param methodName same as className
     * @param paramList
     * @param isStatic Determine if method is static or not
     */
    public PostConditionHandler(String expression, String className, String methodName, String paramList, boolean isStatic) {
        super(expression);
        aspectCode = new StringWriter();
        aspectCodeWriter = new PrintWriter(aspectCode);
        oldVariableNames = new ArrayList<String>();
        this.className = className;
        this.paramList = paramList;
        this.isStatic = isStatic;
	this.methodName = methodName;
    }

    public String getGeneratedAspectCode() {
        return aspectCode.toString();
    }

    public List<String> getOldVariableNames() {
        return oldVariableNames;
    }

    @Override
    public void processExpression() {
        processReturn();
        processOld();
        super.processExpression();
    }

    public void processReturn() {
        expression = expression.replaceAll("\\$return", RETURN_VARIABLE_NAME);
    }

    public void processOld() {
        Pattern oldRegex = Pattern.compile("\\$old\\(([^\\(\\)]*(\\([^\\)]*\\)[^\\(\\)]*)*)\\)");
        Matcher m = oldRegex.matcher(expression);

        StringBuffer processedExpression = new StringBuffer();

        int i = 0;
        while(m.find()) {
            i++;
            String variableName = "old" + i;

            oldVariableNames.add(variableName);

            aspectCodeWriter.print("private ");
            if(isStatic) aspectCodeWriter.print("static ");
            aspectCodeWriter.print("Object " + className + "." + variableName + methodName);
            aspectCodeWriter.println("(" + paramList + ") {");
            aspectCodeWriter.println("return " + m.group(1) + ";");
            aspectCodeWriter.println("}");

            //replace $old(exp) by oldCast(oldi, exp)
            m.appendReplacement(processedExpression, "oldCast"+methodName+"(" + variableName + ", " + m.group(1) + ")");
        }
        m.appendTail(processedExpression);

        if(i > 0) {
            aspectCodeWriter.print("private ");
            if(isStatic) aspectCodeWriter.print("static ");
            aspectCodeWriter.println("<_T> _T " + className + ".oldCast"+methodName+"(Object old, _T exp) {");
            aspectCodeWriter.println("return (_T)old;");
            aspectCodeWriter.println("}");
        }

        expression = processedExpression.toString();
    }

}
